﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Xml.XPath;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;
using MySql.Data.MySqlClient;
using Ocmf.Types;

namespace OCMF_Network_Assistant
{
    public partial class NetworkAssistantMainForm : Form
    {
        private Timer dbWatchDog = new Timer { Enabled = bool.Parse(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbWatchdogEnabled").ToString()), Interval = int.Parse(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbWatchdogInterval").ToString()) };

        private FileSystemWatcher fileSystemWatcher = new FileSystemWatcher((string)Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("ApplicationDataFolder")){IncludeSubdirectories = true};

        private string dbServer = Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbServer").ToString();
        private string dbPassword = Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbPassword").ToString();
        private string dbInstance = Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbInstance").ToString();
        private string dbUsername = Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbUsername").ToString();

        private string experimentFolderPath = Path.Combine(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("ApplicationDataFolder").ToString(),"Experiments");
        private string imagesFolderPath = Path.Combine(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("ApplicationDataFolder").ToString(), "Images");

        private object syncLock = new object();

        public NetworkAssistantMainForm()
        {
            //init gui
            InitializeComponent();
            
            //bind file system watcher event handlers
            fileSystemWatcher.Changed += experimentFileSystemWatcher_Changed;
            fileSystemWatcher.Created += experimentFileSystemWatcher_Changed;
            fileSystemWatcher.Deleted += experimentFileSystemWatcher_Changed;

            //setup timer
            dbWatchDog.Tick += dbWatchDog_Tick;
            dbWatchDog.Start();

            SynchronizeExperiments();
            SynchronizeImages();
            SynchronizeChambers();
        }

        void dbWatchDog_Tick(object sender, EventArgs e)
        {
            SynchronizeExperiments();
            networkAssistantNotifyIcon.ShowBalloonTip(5, "Database Watchdog", "Syncing...", ToolTipIcon.Info);
        }

        public void SynchronizeImages()
        {
            //stops syncing overlaps
            lock (syncLock)
            {
                fileSystemWatcher.EnableRaisingEvents = false;
                //do initial sync
                //get db files
                var dbConnection = new MySqlConnection("SERVER=" + dbServer + ";" +
                                                       "DATABASE=" + dbInstance + ";" +
                                                       "UID=" + dbUsername + ";" +
                                                       "PASSWORD=" + dbPassword + ";");

                var dbCommand = dbConnection.CreateCommand();
                dbCommand.CommandText = "SELECT name FROM images";

                var dbImages = new List<string>();

                dbConnection.Open();
                var dbReader = dbCommand.ExecuteReader();
                while (dbReader.Read())
                    dbImages.Add((string)dbReader["name"]);
                dbConnection.Close();

                //get local files
                var localImages = new List<string>();
                localImages.AddRange(Directory.GetFiles(imagesFolderPath, "*.bmp"));
                localImages.AddRange(Directory.GetFiles(imagesFolderPath, "*.png"));
                localImages.AddRange(Directory.GetFiles(imagesFolderPath, "*.jpg"));
                localImages.AddRange(Directory.GetFiles(imagesFolderPath, "*.gif"));

                for (var i = 0; i < localImages.Count; i++)
                    localImages[i] = Path.GetFileName(localImages[i]);


                //compare db manifest to local manifest and download those which arn't there
                foreach (var image in dbImages)
                {
                    if (!localImages.Contains(image))
                    {
                        dbCommand.CommandText = "SELECT file FROM images WHERE name = @name";
                        dbCommand.Parameters.Clear();
                        dbCommand.Parameters.Add(new MySqlParameter("@name", image));
                        dbConnection.Open();

                        dbReader = dbCommand.ExecuteReader();
                        dbReader.Read();

                        File.WriteAllBytes(Path.Combine(imagesFolderPath, image), (byte[])dbReader["file"]);
                        dbConnection.Close();
                    }
                }

                foreach (var image in localImages)
                {
                    if (!dbImages.Contains(image))
                    {
                        dbCommand.CommandText = "INSERT INTO images VALUES (null,@name,@file)";
                        dbCommand.Parameters.Clear();
                        dbCommand.Parameters.Add(new MySqlParameter("@name", Path.GetFileName(image)));
                        dbCommand.Parameters.Add(new MySqlParameter("@file", File.ReadAllBytes(Path.Combine(imagesFolderPath,image))));
                        dbConnection.Open();

                        dbCommand.ExecuteNonQuery();
                        dbConnection.Close();
                    }
                }
                fileSystemWatcher.EnableRaisingEvents = true;
            }
        }

        public void SynchronizeExperiments()
        {
            //stops syncing overlaps
            lock (syncLock)
            {
                fileSystemWatcher.EnableRaisingEvents = false;
                //do initial sync
                //get db files
                var dbConnection = new MySqlConnection("SERVER=" + dbServer + ";" +
                                                       "DATABASE=" + dbInstance + ";" +
                                                       "UID=" + dbUsername + ";" +
                                                       "PASSWORD=" + dbPassword + ";");

                var dbCommand = dbConnection.CreateCommand();
                dbCommand.CommandText = "SELECT name, lastSaved FROM experiments";

                var dbExperiments = new Dictionary<string, DateTime>();

                dbConnection.Open();
                var dbReader = dbCommand.ExecuteReader();
                while (dbReader.Read())
                    dbExperiments.Add((string) dbReader["name"], (DateTime) dbReader["lastSaved"]);
                dbConnection.Close();

                //get local files
                var localExperiments = new Dictionary<string, DateTime>();
                var localExperimentFiles = Directory.GetFiles(experimentFolderPath, "*.experiment");
                for (var i = 0; i < localExperimentFiles.Length; i++)
                {
                    var xPathDocument = new XPathDocument(localExperimentFiles[i]);
                    var xPathNavigator = xPathDocument.CreateNavigator();

                    var iterator = xPathNavigator.Select("//@LastSaved");
                    iterator.MoveNext();
                    localExperiments.Add(Path.GetFileName(localExperimentFiles[i]),
                                         DateTime.Parse(iterator.Current.Value));
                }

                //compare db manifest to local manifest and download those which arn't there
                foreach (var experiment in dbExperiments.Keys)
                {
                    if (!localExperiments.ContainsKey(experiment) ||
                        localExperiments[experiment] < dbExperiments[experiment])
                    {
                        dbCommand.CommandText = "SELECT file FROM experiments WHERE name = @name";
                        dbCommand.Parameters.Clear();
                        dbCommand.Parameters.Add(new MySqlParameter("@name", experiment));
                        dbConnection.Open();

                        dbReader = dbCommand.ExecuteReader();
                        dbReader.Read();

                        File.WriteAllBytes(Path.Combine(experimentFolderPath, experiment), (byte[]) dbReader["file"]);
                        dbConnection.Close();
                    }
                }

                foreach (var experiment in localExperiments.Keys)
                {
                    if (!dbExperiments.ContainsKey(experiment))
                    {
                        dbCommand.CommandText = "INSERT INTO experiments VALUES (null,@name,@file,@lastSaved)";
                        dbCommand.Parameters.Clear();
                        dbCommand.Parameters.Add(new MySqlParameter("@name", Path.GetFileName(experiment)));
                        dbCommand.Parameters.Add(new MySqlParameter("@file",
                                                                    File.ReadAllBytes(Path.Combine(
                                                                                          experimentFolderPath,
                                                                                          experiment))));
                        dbCommand.Parameters.Add(new MySqlParameter("@lastSaved", localExperiments[experiment]));
                        dbConnection.Open();

                        dbCommand.ExecuteNonQuery();
                        dbConnection.Close();
                    }
                    else if (dbExperiments[experiment] < localExperiments[experiment])
                    {
                        dbCommand.CommandText =
                            "UPDATE experiments SET file = @file, lastSaved = @lastSaved WHERE name = @name";
                        dbCommand.Parameters.Clear();
                        dbCommand.Parameters.Add(new MySqlParameter("@name", Path.GetFileName(experiment)));
                        dbCommand.Parameters.Add(new MySqlParameter("@file", File.ReadAllBytes(experiment)));
                        dbCommand.Parameters.Add(new MySqlParameter("@lastSaved", localExperiments[experiment]));
                        dbConnection.Open();

                        dbCommand.ExecuteNonQuery();
                        dbConnection.Close();
                    }
                }

                fileSystemWatcher.EnableRaisingEvents = true;
            }
        }

        public void SynchronizeChambers()
        {
            var chamberList = new List<Chamber>();
            var dbConnection = new MySqlConnection("SERVER=" + dbServer + ";" +
                                                       "DATABASE=" + dbInstance + ";" +
                                                       "UID=" + dbUsername + ";" +
                                                       "PASSWORD=" + dbPassword + ";");

            var dbCommand = dbConnection.CreateCommand();
            dbCommand.CommandText = "SELECT * FROM chambers";

            dbConnection.Open();

            var dbReader = dbCommand.ExecuteReader();

            while (dbReader.Read())
            {
                chamberList.Add(new Chamber(long.Parse(dbReader["chamberId"].ToString()),
                                            dbReader["hostName"].ToString(),
                                            dbReader["ipAddress"].ToString(),
                                            (ChamberState)Enum.Parse(typeof (ChamberState), dbReader["lastStatus"].ToString()),
                                            DateTime.Parse(dbReader["lastUpdated"].ToString())));
            }
            //Chamber.ToXml("ChamberInventory.xml", chamberList.ToArray());
        }

        void experimentFileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            if(e.ChangeType == WatcherChangeTypes.Deleted && e.Name == "ChamberInventory.xml")
                SynchronizeChambers();
            switch(Path.GetExtension(e.Name))
            {
                case ".jpg":
                case ".bmp":
                case ".png":
                case ".gif":
                    SynchronizeImages();
                    break;
                case ".experiment":
                    SynchronizeExperiments();
                    break;
            }
            
            networkAssistantNotifyIcon.ShowBalloonTip(5, "File Changed", e.Name, ToolTipIcon.Info);
        }

        private void networkAssistantNotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            WindowState = FormWindowState.Normal;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void NetworkAssistantMainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            networkAssistantNotifyIcon.Visible = false;
        }

        private void NetworkAssistantMainForm_Shown(object sender, EventArgs e)
        {
            dbServerTextBox.Text = (string) Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbServer");
            dbInstanceTextBox.Text = (string)Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbInstance");
            dbUsernameTextBox.Text = (string)Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbUsername");
            dbPasswordTextBox.Text = (string)Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbPassword");
            watchdogcheckBox.Checked = bool.Parse(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbWatchdogEnabled").ToString());
            watchdogNumericUpDown.Enabled = watchdogcheckBox.Checked;
            watchdogNumericUpDown.Value = int.Parse(Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).GetValue("DbWatchdogInterval").ToString()) / (1000*60);
        }

        private void okButton_Click(object sender, EventArgs e)
        {
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbServer", dbServerTextBox.Text);
            dbServer = dbServerTextBox.Text;
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbInstance", dbInstanceTextBox.Text);
            dbInstance = dbInstanceTextBox.Text;
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbUsername", dbUsernameTextBox.Text);
            dbUsername = dbUsernameTextBox.Text;
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbPassword", dbPasswordTextBox.Text);
            dbPassword = dbPasswordTextBox.Text;
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbWatchdogEnabled", watchdogcheckBox.Checked.ToString());
            dbWatchDog.Enabled = watchdogcheckBox.Checked;
            Registry.CurrentUser.OpenSubKey(@"Software\OCMF", true).SetValue("DbWatchdogInterval", Decimal.ToInt32(watchdogNumericUpDown.Value * 60 * 1000).ToString());
            dbWatchDog.Interval = Decimal.ToInt32(watchdogNumericUpDown.Value*60*1000);
            WindowState = FormWindowState.Minimized;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Minimized;
        }

        private void checkDatabaseCredentialsButton_Click(object sender, EventArgs e)
        {
            var dbConnection = new MySqlConnection("SERVER=" + dbServerTextBox.Text + ";" +
                                                   "DATABASE=" + dbInstanceTextBox.Text + ";" +
                                                   "UID=" + dbUsernameTextBox.Text + ";" +
                                                   "PASSWORD=" + dbPasswordTextBox.Text + ";");
            try
            {
                dbConnection.Open();
                if (dbConnection.State == ConnectionState.Open)
                    MessageBox.Show("Database Connection Successful.");
                else
                    MessageBox.Show("Database Connection Unsuccessful");
            }
            catch
            {
                MessageBox.Show("Database Connection Unsuccessful");
            }
        }

        private void watchdogcheckBox_CheckedChanged(object sender, EventArgs e)
        {
            watchdogNumericUpDown.Enabled = watchdogcheckBox.Checked;
        }
    }
}
